The WATCOM C/C++ Programmer's FAQ WW WW AA TTTTTTTT CCCC OOOO MM MM cccc / WW WW AAAA TT CC CC OO OO MMMMMMMM cc / WW WW AA AA TT CC OO OO MM MMMM MM cccc / WW WW AA AA TT CC OO OO MM MM MM / WW WW WW AA AA TT CC OO OO MM MM MM / WW WWWW WW AAAAAAAA TT CC OO OO MM MM MM / cccc + + WWWWWWWW AA AA TT CC CC OO OO MM MM MM cc +++ +++ WW WW AA AA TT CCCC OOOO MM MM MM cccc + + Programmer's FFFFFFFFFFFFFFFFFF AAAAA QQQQQQQQQQQQQQ FFFFFFFFFFFFFFFFFF AAAAAAA QQQQQQQ QQQQQQ FFFFFF AAAAAAAAA QQQQQQ QQQQQQ FFFFFF AAAAAAAAAAA QQQQQQQ QQQQQQ FFFFFFFFFFFFFF AAAAAA AAAAAA QQQQQQQ QQQQQQQ FFFFFFFFFFFFFF AAAAAA AAAAAA QQQQQQQ QQQQQQQ FFFFFF AAAAAA AAAAAA QQQQQQQ QQQ QQQQQQQ FFFFFF AAAAAAAAAAAAAAAAAAA QQQQQQQ QQQQQQQQQQQ FFFFFF AAAAAAAAAAAAAAAAAAAAA QQQQQQQQQQQQQQQQQQQ FFFFFF AAAAAA AAAAAA QQQQQQQQQQQQQQQQQQQQ QQQQQQQQQQ Written by Paul Hsieh qed@chromatic.com Revision 2.8 August 4, 1996 (c) Copyright 1996, Paul Hsieh. All Rights Reserved. This document may be freely distributed in any format desired so long as the contents are not modified beyond non-obtrusive formatting. Please note that although I've made a reasonable effort to verify the material contained in this FAQ, I make no guarrantees. The things written here are as true as I know them to be and should not be interpreted as anything more. None of WATCOM, PowerSoft/Sybase or TenBerry systems or any other relevant company has officially endorsed this FAQ. Furthermore, I have no affiliation with WATCOM, PowerSoft/Sybase or TenBerry systems. WATCOM, PowerSoft, Sybase, TenBerry Systems, QNX, IBM, OS/2, Microsoft, Windows, Win32, Direct Draw, Direct X, Intel, Intel's RDX, Metaware, Symantec, Borland, CauseWay, Fastgraph, Varmint's Audio Tools, YackIcons, WordUp Graphics Toolkit, PC Magazine, Soft ICE, Phar lap, PModeW, Team 17 etc., are copyrights/trademarks of their respective owners. ---------------------------------------------------------------------------- Introduction Many/most of the questions dealt with here assume that you already own a version of the WATCOM C/C++ compiler. Ordering information is available on WATCOM's world wide web site. As my personal strengths are in DOS coding, I have slanted this FAQ at DOS programming using WATCOM C/C++. If you are willing to contribute information regarding Windows, OS/2 programming or QNX programming please send it to me and I will include it. Please note that I will only be able to easily verify DOS information (and Windows information, only with great difficulty.) This FAQ mixes advocation with technical information. It has been written this way because after years of posting on USENET this has become my natural style of writing. ---------------------------------------------------------------------------- Frequently asked Questions ~~~~~~~~~~~~~~~~~~~~~~~~~~ These are very commonly asked questions that people ask about WATCOM C/C++. Q01. What is WATCOM C/C++? What does WATCOM C/C++ come with? Q02. Where are WATCOM C/C++'s related web/ftp sites? Q03. Why is WATCOM C/C++ so popular? Q04. What's the syntax for ____? How do I use the ____ tool? How do I make the code generator do ____? I just don't get it. Q05. I think there is a bug in the compiler, what should I do? Q06. Why do so many games use the DOS4GW.EXE DOS extender? Q07. What libraries are available for WATCOM C/C++? Q08. How do I program for Direct X using WATCOM C/C++? Q09. How do I program for Intel's RDX using WATCOM C/C++? Q10. Is the code WATCOM C/C++ produces the fastest/tightest? Q11. How do I use WATCOM's tools to help me optimize my code? Q12. How does WATCOM C/C++ compare with other C compilers? Q13. How do I debug DOS4GW apps under Windows 95? Q14. How do I debug WATCOM apps using Soft-ICE? Q15. How do I enable virtual memory for my DOS4GW application? How do I know exactly what interrupts dos4gw handles all by itself? How do I know exactly what a programmer have to do, using dos interrupts from protected mode? Q16. Why doesn't WATCOM use "_asm" for inline assembly like Microsoft and Borland? Afterall isn't that standard? Q17. Why does my code work only when I compile with debugging info on? Q18. What is FLAT memory model? What is the advantage of a 32 bit compiler such as WATCOM C/C++ over 16 bit compilers? What are the differences? Will I be able to move my 16 bit DOS code straight over to 32 bit? Q19. I am getting this weird error message ... Q20. I am having malloc troubles. It seems that I am trashing the heap but I don't know how to debug it. Q21. How do I write directly to graphics memory? Q22. How do I install a mouse event handler? Q23. What is DPMI and what role does it play in using WATCOM C/C++? How do I communicate between the 16 bit world and 32 bit world on my PC? Q24. What if I really need to compile a 16 bit model program? Q25. Why do WATCOM's results and Visual C++'s results differ on the same source code? Why does WATCOM implement the default signedness of chars different from everyone else? I am having difficulty porting code to WATCOM C/C++. Q26. Can I link external assembly files together with my C files? Q27. How do #pragma's work? How do I do inline assembly language? Q28. How do I do compiled bitmaps? How do I do on-the-fly generated code? Q29. How do I install a 32 bit interrupt vector? How do I install a bimodal interrupt? Q30. How do I get rid of the DOS4GW banner? How do I bind DOS4GW.EXE to my application? How do I get rid of the external DOS4GW.EXE altogether? What other DOS extenders can I use? Q31. How do I make WATCOM C/C++ work with the latest OS/2 Toolkit? Q32. Are there any other WATCOM C/C++ caveats? Q33. Will WATCOM C/C++ support MMX? Q34. So what is your story? Why did you do this? Cool contributions ~~~~~~~~~~~~~~~~~~ These are contributions sent to me that do not correspond to a frequently asked question such as above, but which nevertheless are worth including. C01. Profiling and debugging all rolled into one using an external terminal connected by serial. Contributed by Charlie Wallace. C02. Convert WATCOM's help file format to a text file for easier reading and potentially for easier manipulation. ---------------------------------------------------------------------------- Questions with answers ~~~~~~~~~~~~~~~~~~~~~~ Q01. What is WATCOM C/C++? What does WATCOM C/C++ come with? A01. WATCOM C/C++ is an Intel x86 based ANSI C++ and C compiler. It supports various target operating systems. Its most notable and exercised strength is its 32 bit DOS based support. WATCOM has established its reputation as the makers of a world leading C compiler and has been in business for about 10 years. You can get a more complete description of the compiler on the WATCOM web site, however here are the most notable things that are included: - 32 and 16 bit compilers, with standard libraries and DOS4GW.EXE. - Additional DOS graphics and x86 specific libraries. - Library manager and linker. - All flavours of Windows programming including 16 and 32 bit MFC support as well as Win32. (Includes Windows SDK help.) - Numerous Windows development tools (dialogue editors and so on.) - Support for Dos extenders, OS/2, QNX, AutoCAD and Novell NLMs. - IDE: Editor, Debugger, Execution Sampler/Profiler, Make, Touch. - Online documentation and sample code. ---------------------------------------------------------------------------- Q02. Where are WATCOM C/C++'s related web/ftp sites? A02. These are the URLs that I am aware of: WATCOM's home page http://www.powersoft.com/products/languages/watccpl.html WATCOM C/C++ readmes ftp://ftp.powersoft.com/pub/c_cpp/maintfls WATCOM C/C++ goodies http://www.powersoft.com/services/files/c_gen.html KNUT's WATCOM C/C++ tutorial http://www.oslonett.no/home/oruud/wat_tut.htm X2FTP site: ftp://x2ftp.oulu.fi/pub/msdos/programming/watcom Official site for the WATCOM C/C++ FAQ http://www.geocities.com/SiliconValley/9498/watfaq.txt FTP site for downloading the WATCOM C/C++ FAQ ftp://x2ftp.oulu.fi/pub/msdos/programming/watcom/watfaq21.zip Previous WATCOM C/C++ FAQ ftp://x2ftp.oulu.fi/pub/msdos/programming/watcom/watcom.07 Miscellaneous WATCOM C/C++ and OS/2 device driver notes http://www.wdi.co.uk/os2dd/QA__0009.htm Email List server for WATCOM users WATCOM-L@umdd.umd.edu send a message to listserv@umdd.umd.edu with the first line of your message containing: "SUBSCRIBE WATCOM-L John Doe" to subscribe. USENET Newsgroups news:alt.msdos.programmer news:comp.os.msdos.programmer news:rec.games.programmer For tutorials on C/C++ (not specifically WATCOM) I have the following URLs: C tutorials http://www.strath.ac.uk/CC/Courses/NewCcourse/ccourse.html http://www.iftech.com/classes/c/c0.htm ftp://x2ftp.oulu.fi/pub/msdos/programming/docs/teach-c.zip C++ tutorial http://www.iftech.com/classes/cpp/cpp0.htm ---------------------------------------------------------------------------- Q03. Why is WATCOM C/C++ so popular? A03. WATCOM C/C++ is a very good tool. It was the first pure 32 bit C/C++ compiler widely available for x86 platforms. It is a cross platform, high performance object code generator with no shortage of support tools. It is on these merits that WATCOM has sustained its popularity. Somewhere around 1992-93, WATCOM decided to drop the price (to a mere $400 Cdn vs. the old price of $1200 US) and started advertising more heavily in magazines such as BYTE and PC Magazine. Around this time I was heavily advocating it (version 9.0) it in newsgroups such as "rec.games.programmer" as the premiere C compiler for high performance and reliability, making such bold statements as: "WATCOM C/C++ will produce code which is at *least* twice as fast as your current 16 bit compiler, and more typically around five times as fast" which was not stretching the truth as far as I knew it. Flame wars ensued where the focus was not only Borland and Microsoft, but WATCOM. Then a single program was introduced that brought immediate and serious attention to this compiler: DOOM. What is this magical "DOS4GW" thing that makes DOOM so good? Were the DOOM programmers serious when they said they used almost no assembly language? What C compiler could they have possibly used that could pump that much game onto the screen? Soon, more and more games were made, hailing the telltale "DOS4GW" banner. A few years later PC Magazine ran a comparative study of a collection of C compilers. They hailed WATCOM C/C++ v9.5 for producing the fastest code (by a *wide* margin) but criticized it for its klunky command line driven interface. Since then, many game companies switched and dozens of titles appeared with the "DOS4GW" banner displayed proudly including: Raptor, System Shock, Descent, Magic Carpet, NASCAR Racing, Terminal Velocity etc. 32 bit was finally becoming a reality on the PC, and it had nothing to do with "Windows" or "OS/2". But even IBM was getting smart. Numerous OS/2 utilities and parts of the OS itself were compiled using WATCOM C/C++. With version 10.0 WATCOM introduced an IDE, added support for MFC and added an assembler. With 10.5 they decided to out source the new and improved IDE to an award winning third party developer, included better support for Win 95 and have built in a higher degree of Visual C++ compatability. With its licensing of MFC and ability to do ... well just about anything, WATCOM has clearly set its sights on one goal: To become the defacto standard C/C++ compiler for PCs. Even Microsoft dares not ignore WATCOM C/C++; "Direct X" is compatible with WATCOM C/C++ (this was no doubt at the demand of the 3rd party BETA testers.) ---------------------------------------------------------------------------- Q04. What's the syntax for ____? How do I use the ____ tool? How do I make the code generator do ____? I just don't get it. A04. The first place to look when trying to deal with general questions is the online help. In a DOS box you can access the help via: WHELP help_file [topic_name] If you've installed the compiler to use the CDROM, be sure to have the WATCOM CDROM in the CDROM drive. It takes a little digging but there is a lot of online information available. If you give no parameters, most of the tools will respond with proper command line syntax. Additionally, the file %WATCOM%\DOS4GW.DOC gives you information on configuring DOS4GW.EXE for certain systems and for using virtual memory under DOS. Remember, that the WATCOM C/C++ compiler was not designed with beginners in mind. If you are just starting to learn C/C++ then I is suggest you get plenty of reading on C/C++ in general and OS-specific programming manuals (I.e., DOS, Windows, OS/2, QNX, or whatever target platform you are interested in building for). For a deeper understanding of WATCOM C/C++ you'll need to learn at least bits and piece what object files, libraries, dll's, makefiles, and protected mode are. Experienced programmers find that WATCOM C/C++ doesn't have any real brick walls that other compilers have, but that coaxing it to do what you want it to do often takes some patience and perseverence. That is the issue this FAQ is trying to address. In general your pursuit of information when deal with difficulties using the WATCOM C/C++ compilers should be in the order of: (1) Check the online help files, (2) Check reference materials such as a DOS or Windows programming books, as well as this FAQ, (3) Post a question to the internet (including the USENET newsgroups, or the WATCOM listserver listed above) (4) Contact WATCOM directly. ---------------------------------------------------------------------------- Q05. I think there is a bug in the compiler, what should I do? A05. You'd be surprised by the number of "compiler bugs" people think they've found. Stop. Take a deep breath and think about it for a moment. The WATCOM C/C++ compiler is generally a very stable tool. Is your code proper ANSI C? Are you violating the optimization assumptions assumed by your choice of compiler switches? (Aliasing is the only one really at issue.) Of the twenty or so "compiler bugs" that I thought I found, only two of them were ever even close to being real compiler bugs (see "Q32. Are there any other WATCOM C/C++ caveats?" Numbers 3 and 6.) The fact that something works on one compiler but not another is not necessarily an indication that one compiler is not functioning properly. For example, another compiler may not buffer writes to stdout via printf. So the fact that printf("Hello world"); echos immediately on one compiler and not at all on another (WATCOM C/C++ buffer's writes, so indeed its an example of a compiler that would not output anything) is not an indication that one or the other compiler is broken. In general, you are supposed to call them and ask them for assistance, but these days, with their compiler being as popular as it is, it may be difficult to get satisfactory responses from their customer support. You may first try your luck on the newsgroups listed at the top of this FAQ, if you have USENET access. Powersoft/WATCOM themselves have set up a WWW bug report service on their web page where you can submit problem reports. In general, if they think your bug is nontrivial, then they will ask you to send them sample code which isolates the problem. WATCOM provides a "shroud" utility that will obfusticate your code if you are worried about unwanted distribution of your sources. It is available at the following URL: ftp://ftp.powersoft.com/pub/c_cpp/general/shroud.zip However, I would highly recommend that if feasible, you simply duplicate the problem in a clean project that does not expose any sources that you don't want exposed. Calling them for assistance should be a last resort, but if you feel that you have found a genuine bug and have exhausted all reasonable avenues then you should not hesitate to tell them about it. When and if they fix the problem, the compiler will be a better tool for all of us. When there are bugs in the compiler that WATCOM is aware of and have fixed, they usually provide a patch that is available from their www and ftp sites (listed above.) ---------------------------------------------------------------------------- Q06. Why do so many games use the DOS4GW.EXE DOS extender? A06. DOS4GW.EXE is the royalty free redistributable 32 bit DOS extender that comes with WATCOM C/C++. DOS4GW.EXE is made by "Tenberry Software" (formerly Rational Systems) specifically for WATCOM. Although alternative DOS extenders with more/different features exist, users of WATCOM C/C++ for DOS programming (that is to say, game programmers) will tend to use DOS4GW by default. (It also has brand name recognition which may lead some programmers to desire the telltale DOS4GW banner.) ---------------------------------------------------------------------------- Q07. What libraries are available for WATCOM C/C++? A07. I don't have a comprehensive list, but I know that Varmint's Audio Tools, Fast Graph, YackIcons, WordUp Graphics Toolkit, ZSVGA, Microsoft's Direct X based Games SDK and Intel's RDX are notable libraries that come in WATCOM C/C++ friendly flavors. In their latest versions, WATCOM has really gone out of their way to make themselves more compatible with Visual C++, which leads me to believe that many libraries out there which have been compiled for Visual C++ will also work with WATCOM C/C++. Needless to say there is no shortage of libraries out there that support WATCOM C/C++. The WATCOM web site lists numerous 3rd party developers that are supporting their C/C++ compiler. Here's a list of some stuff available for WATCOM C/C++: Graphics libraries WGT Watcom ftp://x2ftp.oulu.fi/pub/msdos/programming/wgt51.zip Xlib Watcom ftp://x2ftp.oulu.fi/pub/msdos/programming/xlib/xlib06p2.zip Watcom ModeX ftp://x2ftp.oulu.fi/pub/msdos/programming/watcom/w_modex.zip JLIB ftp://x2ftp.oulu.fi/pub/msdos/programming/djgpp2/jlib_1-5.zip Intel's RDX http://www.intel.com/ial/rdx/ Zephyr's ZSVGA http://www.phoenix.net/~balkum/products/zpmsvga.html gkit05b.zip ftp://x2ftp.oulu.fi/pub/msdos/programming/watcom/gkit05b.zip 3DGPL http://www.cs.mcgill.ca/~savs/3dgpl.html Audio libraries VAT Watcom ftp://x2ftp.oulu.fi/pub/msdos/programming/mxlibs/vatpm061.zip SMIX Watcom ftp://x2ftp.oulu.fi/pub/msdos/programming/mxlibs/smixw125.zip DSIK Watcom ftp://x2ftp.oulu.fi/pub/msdos/programming/mxlibs/dsik205.zip ---------------------------------------------------------------------------- Q08. How do I program for Direct X using WATCOM C/C++? A08 I am not a Windows programming expert but the following information appeared on the USENET: >How well does the Games SDK work with Watcom? If well, how many >trials did it take to get the 'magic options' set up right? Works well, so far. I did have to use the windows.h file supplied with the Games SDK, and that was hidden somewhere under the SAMPLES subdirectory. I also had to define a couple of macros to get the samples to compile. The whole thing took about 4 hours. But then Ed Mueller said: With 10.6, you no longer need to include windows.h supplied with the Games SDK. The macros you need to define are NOIME and NOMCX. And Marty (mmallick@watcom.on.ca; hey! An employee of WATCOM!) said: In response to questions regarding the WATCOM C/C++ compiler and DirectX: Watcom C/C++ 10.6 and DirectX 2.0 FYI: The newly released DirectX 2.0 CD has a WATCOM directory that contains a zip file that will make all of the necessary changes to the sample files to work with Watcom C/C++ version 10.6. Once these changes are made, the makeallw.bat batch file can be run to make the sample programs. See the readme file in the WATCOM directory of the DirectX 2.0 CD for more information. "Avocado Green" (truongmn@UCUNIX.SAN.UC.EDU) wrote the following on the WATCOM List server: Here are some simple steps in using the Watcom IDE (10.6) to compile and run the DirectDraw example (DDEX3) included with the DXSDK. I thought I'd save someone a few hours from messing with the make files. 0. Run the Watcom IDE (run it from the DOS box command line if it'd make you feel less guilty.) 1. Select menu option "File->New Project" Select the C:\DxSDK\Samples\DDex3 directory and enter "ddex3.wpj" as your project name Note: If you installed the DirectX SDK in another directory other than C:\DxSDK, make the changes as appropriate. 2. Enter "DDex3" as the target name, "Win32" as Target Environment, and "Windowed Executable [.exe]" as the Image Type. 3. Select menu option "Targets->Target Options->Windows Linking Switches" Go to "2. Import, Export and Library Switches", In field "Library directories(;):[libp]" enter "..\..\lib" In field "Libraries(,):[libr]" enter "ddraw.lib" 4. Add these source files into your project: ..\misc\ddutil.cpp ddex3.cpp ddex3.rc 5. Highlight the "ddex3.cpp" file and select menu option "Sources->Sources Options->C++ Compiler Switches" In field "Include directories:[-i]" add ";..\..\inc;..\misc;..\include" so it should read "$(%watcom)\h;$(%watcom)\h\nt;..\..\inc;..\misc;..\include" ...of course you've set your Watcom environment variables already, right? 6. Repeat step 5. for ddutil.cpp also, or you can perform step 5. on the folder (.CPP) (representing all .CPP files) 7. Select menu option "Target->Make" and watch the few warning mesages go by 8. Select menu option "Target->Run" and enjoy your Watcom compiler running something other than "helloworld.c" (well, for me anyway) Other Direct X specific FAQ's may best be answered at the following URL: http://www.microsoft.com/mediadev/msupport/mergfaq.htm#DirectDraw ---------------------------------------------------------------------------- Q09. How do I program for Intel's RDX using WATCOM C/C++? A09. Intel's RDX is a library for composing sprites, playing back sound and video. I don't know the details, however Hossain Morshed of Intel Corp. has responded to the inquiries of Ed Mueller and what appears to be the popularity of this FAQ with the following information: The following is a description of how RDX can be used with Watcom compiler. Thanks, Hossain Morshed Intel Corp. Intel(R) RDX and Watcom* C/C++ 06/12/96 The RDX v2.1 Developer's Kit has been tested with Watcom C/C++ (v10.6). It should be available for download this June (6/96) from the Intel RDX Web Site (http://www.intel.com/ial/rdx/). If you are using Watcom C/C++ with the RDX v2.1 DLLs, you will need to link your application to Watcom specific import libraries. You can create Watcom import libraries for the RDX DLLs by using the Watcom WLIB tool. For example, the following commands create the necessary import libraries (provided that WLIB is in your path and the DLLs reside in the C:\RDX\SYSTEM\RELEASE directory): wlib dmix.lib +c:\rdx\system\release\dmix.dll wlib dinoav.lib +c:\rdx\system\release\dinoav.dll wlib dino2d.lib +c:\rdx\system\release\dino2d.dll If you are trying to use Watcom C/C++ with the RDX v2.0 DLLs, we suggest you use the RDX v2.1 Developer's Kit. Besides providing many new features and enhancements, it will also allow you to link your applications successfully to the data structures that the DLLs export. However, if you want to use Watcom C/C++ with RDX v2.0, it is possible to do this successfully by modifying the DINO.H and DINOAV.H header files so that they declare the FX GUIDs using the DEFINE_GUID macro all of the time and don't specify the DINO_SYMBOL/DINOAV_SYMBOL prefix. Then all you have to do is modify one of your application's C or C++ modules to include the INITGUID.H header file before inclusion of DINO.H and DINOAV.H. This will cause the FX GUID data structures to be declared in that module and thus be available to all of the other modules in your application. Note: To successfully compile the RDX v2.0 sample programs a similar modification is necessary, in addition to a few other minor changes due to differences in how Watcom C/C++ and Microsoft Visual C++* compile source code. The RDX Developer's Kit does not provide Watcom specific make files for the sample programs. * Other product and corporate names may be trademarks of other companies and are used only for explanation and to the owners' benefit, without intent to infringe. It appears that to use RDX, you must enter into an agreement with Intel that involve NDA's of some sort. Refer to the URL given above for more information. "Avocado Green" (truongmn@UCUNIX.SAN.UC.EDU) wrote the following on the WATCOM List server: Here are some simple steps in using the Watcom IDE (10.6) to compile and run the Intel RDX 2.1 (2.0 did not work fully with Watcom) example (EVENTS) included with the SDK. Note: The "lite" version of the RDX 2.1 SDK does not contain the .bmp files needed to build the examples, so you'd probably want to ask Intel for their RDX CD instead of downloading the 63MB archive. I'm not sure if Intel has got v2.1 on CD yet, so if you have v2.0, install that, then install the 2.1 "lite" version on top of it. 1. Create the Watcom import library files by running wlib like so: - cd to \RDX\Lib (if you installed the SDK there) - move the .lib files there to some other directory, you should have nothing in \RDX\Lib - run: - wlib dmix.lib +c:\RDX\SYSTEM\RELEASE\dmix.dll - wlib dinoav.lib +c:\RDX\SYSTEM\RELEASE\dinoav.dll - wlib dino2d.lib +c:\RDX\SYSTEM\RELEASE\dino2d.dll - wlib rdxam.lib +c:\RDX\SYSTEM\RELEASE\rdxam.dll There's a \RDX\SYSTEM\DEBUG directory with these same .DLL, Guess these are the debuging version of the .DLLs 2. Run the Watcom IDE (run it from the DOS box command line if it'd make you feel less guilty.) 3. Select menu option "File->New Project" Select the C:\RDX\Samples\Events directory and enter "Events.wpj" as your project name 4. Enter "Events" as the target name, "Win32" as Target Environment, and "Windowed Executable [.exe]" as the Image Type. 5. Select menu option "Targets->Target Options->Windows Linking Switches" Go to "2. Import, Export and Library Switches", In field "Library directories(;):[libp]" enter "..\..\lib" In field "Libraries(,):[libr]" enter "dmix.lib dino2d.lib" 6. Add these source files into your project: events.c help.c winmain.c events.rc 7. Highlight the folder (.C) (representing all .C files) and select menu option "Sources->Sources Options->C++ Compiler Switches" In field "Include directories:[-i]" add ";..\..\include" so it should read "$(%watcom)\h;$(%watcom)\h\nt;..\..\include" 8. Select menu option "Target->Make" - wow not even a warning message. 9. Select menu option "Target->Run" - Kill the dweebs ---------------------------------------------------------------------------- Q10. Is the code WATCOM C/C++ produces the fastest/tightest? A10. The WATCOM C/C++ compiler is generally regarded as among the best in terms of code generation. For example, it produced significantly better code than any 16 bit compiler. In an article in PC Magazine a few years back, benchmarks indicated WATCOM C/C++ version 9.5 had the tightest and fastest code among such compilers as Visual C++, Borland C/C++, Metaware Hi C, Zortech and Symantec (by a wide margin). WATCOM's compilers have generally lead in the performance category in all brands and versions of their compilers starting with version 7.x, however among the more modern compilers it appears as though both "djgpp" and "Visual C++ 4.x" produce pure C code compilation which is as good (if not slightly better) than WATCOM's. However, among the reasons for chosing WATCOM for high performance code generation is also its ability to use inline assembly language, the disassembly tool and its ability to collect accurate performance profiling information. These tools are an indespensible part of the a "pedal to the metal" coder. Be sure to follow the recommended switches for compiling your code that are given in the documentation. Options such as "assume no aliasing" and using "reciprocal and multiply" for floating point divides can cause the compiler to make assumptions about your code that have great performance impact even though the code generation techniques fall outside the rules of the ANSI spec. You should be sure you understand all the options for optimization. ---------------------------------------------------------------------------- Q11. How do I use WATCOM's tools to help me optimize my code? A11. WATCOM comes with 3 tools that make the code optimization process an easy one: Execution sampler/profiler, disassembler and debugger. There is a commonly held belief that in most applications 10% of your code will be executed 90% of the time. That is to say, most applications tend to be bottlenecked, with the tightest bottleneck causing the greatest performance penalties. With WATCOM C/C++ this still holds true, but not to such a severe extent, since WATCOM tends not to produce any "bottle- necky" code. It is for this reason that your first step in your optimization phase should be to profile your code. Even if your project is half complete, and not representative of its final form, execution sampling and profiling can often pinpoint unexpected bottlenecks in your code. The execution sampler works by executing along with your code, randomly gathering instruction addresses during its run. The profiler then converts the sample data into a chart which clearly shows what functions were called. You can zoom into each function to see what lines of code are being executed, and keep doing that right down into assembly level. To do this of course you have to compile your code with line number debugging information (/d1 or /d2.) Not too surprisingly, data collected on Pentiums may show a sample data on machine instructions one after where the real execution occurred. This is because the Pentium tries to execute instructions in pairs and the delays in one instruction may appear to be passed to the instruction it is paired with. The other tool is the disassembler. Using it you can see all the instructions compiled and correlate them their source lines. WATCOM C/C++ generally only uses 1 cycle instructions. However, in order to strike a reasonable balance between code size and performance, WATCOM also uses some complex instructions. The most notable being: mul, imul, div, idiv, movzx and movsx. Simply scanning for these instructions can help you see where your data structures and type mismatching are causing sub-optimal code to be produced. Finally the debugger is a simple tool not only for catching your bugs but for tracing through your code to see the path it is really taking. Most of the time the execution path for small projects is fairly clear to the programmer. But when multiple programmers are involved or your code becomes very fragmented, it is not so clear. Very often, simply tracing through your programs can lead you into good insights about the performance of your program. These general principles can be applied to any compiler, however few compilers can rival WATCOM in these tools. For information about Pentium optimizations in general, Agner Fog has written up an excellent reference work which can be found on the following URL: http://www.geocities.com/SiliconValley/9498/p5opt.html ---------------------------------------------------------------------------- Q12. How does WATCOM C/C++ compare with other C compilers? A12. WATCOM C/C++'s biggest strengths right now are the performance tools and its ability to do cross-platform development. With WATCOM the concept of a single project targetting multiple platforms is a practically realizable goal. Their 32 bit compilers have been around since about 1992, years before Borland, or Microsoft had PC based 32 bit compiler technology (apparently DJGPP/DJGCC had it, but was highly obscure by comparison.) However, their recent acquisition by PowerSoft has given them a highly acclaimed RAD tool (Optima++) that is based on top of the WATCOM C/C++ compiler; a joining of technologies that has caught their competitors with their pants down. By comparison, Microsoft is currently only supporting VC++ which can create targets for Windows and DOS. An extra dos extender (at extra cost) is required for 32 bit DOS code (I believe Pharlap's TNT is the only one really supported.) DOS development is not supported by the IDE. Version 4.x requires 24MB of memory to run smoothly (as opposed to WATCOM's requirement of 8MB.) Microsoft has gotten into a habit of tweaking their compilers and operating systems in lock step; creating more and more conventions and odd things like "Thunking" (a Windows OS and VC++ specific way of performing 16<->32 bit execution segment translations.) Users of Microsoft compilers (and products in general) become locked into their level of technology be it good or bad. They also get locked into their "vision" as they are clearly pointing developpers to Windows 95 and Windows NT. Of course OS/2, QNX and other platforms will never be supported. The code generator for version 4.x compares favorably with WATCOM's, but the lack of reliable performance tools and the klunky out-dated method of inline assembly keeps VC++ from being the performance compiler of choice. Borland is looking straight into the abyss these days. I am not very familliar with their most recent products, but their strength has always been their IDE and compile speed. But the latest WATCOM C/C++ compilers have closed these gaps considerably. Borland attempted to diverge from MFC with "OWL". They also stopped support for DOS for a brief period. Their PowerPak solution does not completely map in the first megabyte of memory which is problematic, and requires a lot of support to work around. Last I heard, Borland had dropped PowerPak support completely. These mistakes have cost Borland valuable time and their compiler technology has suffered as a result. They themselves have practically admitted this and have thrown their efforts behind "Delphi" (a RAD tool layered on top of their non-standard Object Pascal.) DJGPP beats all these compilers in two places: code generation and price. (It appears to produce the tightest code, and costs $0.) However, it falls flat on its face in terms of compile speed, interface and third party support. DJGPP has only recently been robust enough to produce targets that work under both DOS and Windows DOS boxes, and the currently available Windows native 32 bit support is still in development stages (and is a separate compiler.) The IDE is still in development and don't count on MFC support. User support is there through the internet. Standard game libraries such as WGT, Fastgraph and Direct Draw have not been ported, and are not likely to ever be. (On the other hand, SVGAKit from SciTech has been ported, very recently.) I haven't heard so much as a peep from Metaware. As for Symantec, I hear they are doing java. Its likely that in the face of VC++ these companies felt they simply couldn't compete. Indeed its hard to see how any other compiler besides WATCOM C/C++ will survive Microsoft's latest product juggernaut. ---------------------------------------------------------------------------- Q13. How do I debug DOS4GW apps under Windows 95? A13. WATCOM implements this using two DOS boxes. This scheme allows the target program to retain full control of its VM without interference from the debugger (for, example, you should be able to debug apps which take over the screen without any problems besides Window's ability to virtualize that mode.) So in theory, debugging should be more reliable. In one DOS box you run the windows server: winserv -trap=rsi and in the other you run the debugger: wd -trap=win This is essentially the same procedure used for remote debugging over a serial cable. Be sure that the [386enh] section of your system.ini contains the line "device=wdebug.386". If you have problems try explicitely entering the path for wdebug.386 (it should be %WATCOM%\binw) and make sure that this file actually exists. Be sure you are running wd.exe from the %watcom%\binw directory, not the %watcom%\binnt directory. The "rsi" thing stands for "Rational Systems incorporated" which is the former name of TenBerry Systems, so don't be surprised if this changes to "tbs" in future versions of WATCOM C/C++. For Windows NT, the server should be run as "vdmserv -trap=rsi" and the debugger should be run as "wd -trap=vdm ". Similar solutions exist for other OS's (Thanks to Eric Kenslow, "Gothmog" and Tomas Likens for the majority of this information.) ---------------------------------------------------------------------------- Q14. How do I debug WATCOM apps using Soft-ICE? A14. Richard Horrocks writes: I don't know about 9.5, but for 10.0+ just compile with "-d3 -hc", link with "debug codeview opt cvpack", then run DBG2MAP on the executable and finally MSYM to get a symbol file that SoftIce can read (I can't verify that this works with source line numbers as I get "out of memory load source file" errors). As far as I know, SOFT-ICE's symbol memory can be expanded by setting the 'SYM=###' in the *.DAT configuration file (in SOFT-ICE/W its the WINICE.DAT) Refer to the SOFT-ICE documentation for more information. ---------------------------------------------------------------------------- Q15. How do I enable virtual memory for my DOS4GW application? How do I know exactly what interrupts dos4gw handles all by itself? How do I know exactly what a programmer have to do, using dos interrupts from protected mode? A15. These and other DOS4GW specific questions are answered in the file %WATCOM%\DOS4GW.DOC, and in the online help under the DOS/4GW section of the user's guide. ---------------------------------------------------------------------------- Q16. Why doesn't WATCOM use "_asm" for inline assembly like Microsoft and Borland? Afterall isn't that standard? A16. Well, first off, that most definately is *not* standard (as in ISO or ANSI.) Since the Microsoft/Borland _asm construct falls outside the ANSI standard, it promotes the generation of C sources which are not portable. WATCOM instead uses the ANSI sanctioned "#pragma" directive. (See discussion latter for why WATCOM's method is intrinsically more portable than the _asm method.) The file: ftp://ftp.powersoft.com/pub/c_cpp/general/asm.txt explains WATCOM's reasoning more fully. ---------------------------------------------------------------------------- Q17. Why does my code work only when I compile with debugging info on? A17. Dom Laflamme asked this question on the USENET newsgroup "rec.games.programmer" and I gave the following response: Dom Laflamme wrote: > Hi, > I've been coding a game engine for the passed 3 months. It works > fine and it's fast. Execept that when I turn the "Debugging Info" off > in Watcom, _nothing_ works, it just crashes. Since I cant debug the > thing properly because of lack of debuging info, I face a truly > problematic situation! There is a big difference in the code generated by using the /d2 debug switch and no debugging. /d1 is supposed to be nearly identical to using no debugging but has significantly less deugging information. WATCOM's optimizations are *NOT* broken as someone else posted. But they are truly aggressive. WATCOM recommends setting: /otexan, and each switch (t, e, x, a, and n) is sufficiently documented in the online help. I'm guessing that you have these setting all on since "it's fast". This recommendation is based on the assumption that you are writing correct ANSI-C and that you are not stepping outside the bounds to the point of breaking one of WATCOM's optimization strategies. As a simple test, trying setting all the optimization switches off (as well as the debugging switch off.) If your engine starts working again, then turn the optimization switches back on one at a time until your engine breaks again. Keeping in mind that the "a" switch (assume no aliasing) is the only one in which correctly written (but really brain dead) ANSI C can fail to execute. Once you have isolated the switch, read the documentation on the switch very carefully. Try to isolate the problem area and disassemble the code with each optimization switch. Once you see the difference, you should be able to figure it out. You problem is likely one of three things: (1) Simple mistake, like dereferencing a bad pointer, or accessing outside the bounds of an array, (2) You are misunderstanding the meaning of the "volatile" ANSI C keyword and hence are misusing it, or not using it (the most likely) when you should. (3) You are aliasing pointers to non-disjoint structures and WATCOM is reordering an inner loop in too aggressive a manner (you will have to either recode or remove the "a" switch entirely.) I have seen all three cases in my own work and in the work of others. I found that using WATCOM is an exercise in "re-learning" how ANSI C really works. ---------------------------------------------------------------------------- Q18. What is FLAT memory model? What is the advantage of a 32 bit compiler such as WATCOM C/C++ over 16 bit compilers? What are the differences? Will I be able to move my 16 bit DOS code straight over to 32 bit? A18. WATCOM C/C++ can compile for either 16 bit or 32 bit. The compilers are named wpp/wcc/wcl and wpp386/wcc386/wcl386 respectively for 16 bit and 32 bit compiliation. Every DOS programming model under the sun is implemented: TINY, SMALL, COMPACT, MEDIUM, LARGE, HUGE (the 16 bit models) as well as FLAT and LARGE32 (the 32 bit models). WATCOM C/C++'s default 32 bit DOS memory model is FLAT memory model. FLAT memory model is an analogue of TINY model for 32 bit programs. ES and DS are set to the same selector which points at the base of memory and maps all 4GB of address space. CS is a selector that points to all of memory like DS and ES, but has the execution attribute set, meaning that it cannot be written to directly. SS is set to a special stack segment to make stack overflow easier to detect. This set up allows you to, in nearly all situtations, ignore segment/selector registers completely. FS and GS are available for applications to use as they see fit and by default are set to 0. (Hint: Be sure to check the values of these selectors whenever you are debugging a failure in your code. These selectors may change as a result of ill-behaved interrupt vector calls, and hence may need to be saved and restored properly (see the #pragma discussions elsewhere in this FAQ)) Unless you are dealing with interfacing to external 16 bit resources (such as APIs implemented through interrupts) you never need to deal with 64K segment limits, or strange keywords like FAR, NEAR or MK_FP. Pointers and int's are both 32 bit, and while its not advisable, you can cast between the two and still be portable to most platforms (16 bit x86 environments and 64 bit DEC Alpha environments being the most notable exceptions.) The extra bandwidth/precision you get for using a 32 bit coding model over a 16 bit one speaks for itself. 32 bit instructions executed from 32 bit segments do not need instruction overrides. Beyond that only TINY and SMALL models (from the 16 bit model choices) compare with the low overhead for context switches. That is to say, the only 16 bit models that also don't require selector loads for new data pointers or function calls are restricted to 64K of data and/or code. Familliar DOS/BIOS data areas such as 0A000:0000 and 004f:0000 have been mapped in the most natural way: 16*segment+offset. So VGA space is now at _FLAT:0x0A0000 and the standard BIOS area is now at _FLAT:0x04F0. But this mapping doesn't necessarily hold true for the rest of system memory. This is actually a good thing since it allows for linearly mapped graphics cards to have their address range placed at a convenient place, namely the end of your conventional memory. (Or in the case of Windows; usually a special address range with certain OS specific characteristics.) (It is important to note that the construct MK_FP(0xA000,0); doesn't have the same meaning and will *NOT* correctly map the screen to a usable pointer. Use (char *)(0x0A0000) as just described.) This sudden ease of implmentation for everything is actually likely to make the job of porting from 16 bit DOS quite involved for many applications. This porting procedure would mostly be an exercise in *undoing* all the 16 bit nonsense that was being done previously. ---------------------------------------------------------------------------- Q19. I am getting this weird error message ... A19. Lets go through some of the more common ones: (1) When I run my program I get something like: DOS/4GW Protected Mode Run-time Version 1.97 Copyright (c) Rational Systems, Inc. 1990-1994 DOS/4GW error (2001): exception 0Eh (page fault) at 237:825A401F TSF32: prev_tsf32 5290 SS 23F DS 23F ES 23F FS 0 GS 87 EAX FFFFFFFF EBX 8244A380 ECX 0 EDX 0 ESI 824493A1 EDI 8244939C EBP 8244A374 ESP 8244A370 CS:IP 237:825A401F ID 0E COD 6 FLG 10202 CS= 237, USE32, page granular, limit FFFFFFFF, base 0, acc CFFB SS= 23F, USE32, page granular, limit FFFFFFFF, base 0, acc CFF3 DS= 23F, USE32, page granular, limit FFFFFFFF, base 0, acc CFF3 ES= 23F, USE32, page granular, limit FFFFFFFF, base 0, acc CFF3 FS= 0, USE16, byte granular, limit 0, base 0, acc 0 GS= 87, USE16, byte granular, limit FFFF, base 18DD0, acc F3 CR0: unavailable Crash address (unrelocated) = 1:0000001F This means that you have an error in your program. If the address given in the last like looks like 1:000????? (basically segment 1 and a reasonably small sized offset) then chances are you can run this under the debugger and have the address of your error shown to you. If even the address looks like its off in the weeds, remember that WATCOM's debugger allows you to crawl up the execution stack to see how the crash was arrived at. If running the debugger is out of the question or somehow problematic for you then you can generate a map file by putting "option map=myprog" on your link command line, or putting "/fm=myprog" on your wcl command line. This is most useful when you are compiling with debug info ("/d2" on your compile command line.) The resulting myprog.map file is correlate the line numbers in your program to the "Crash address" given above. Often the target address will be in a library function. This generally means that either the parameters passed to the function are bogus, or some global state that the function depends on has become damaged. Global state is hard to be debug, but not impossible. See "Q20. I am having malloc troubles. It seems that I am trashing the heap but I don't know how to debug it." later on in the FAQ. But most of the time you are simply accessing memory you shouldn't be (past end of an array, stuffing a pointer without first mallocing it, inadvertantly corrupting a pointer that you use later.) Remember, pointers are not in "far" format, but rather in "_FLAT" format. So 0x0A0000000 is not the address of the VGA frame buffer, 0x0A0000 is. (2) I type: wcl386 /l=dos4g hello.c and get back: WATCOM Linker Version 10.6 Copyright by WATCOM International Corp. 1985, 1996. All rights reserved. WATCOM is a trademark of WATCOM International Corp. Warning(1107): file __WCL__.LNK: line(2): undefined system name: dos4g loading object files searching libraries Error(3002): ** internal ** - format not decided Error: Linker returned a bad status This means that when you installed WATCOM C/C++ you did not select 32-bit DOS target. You can run the installation procedure again to do an incremental install of 32 bit DOS compiler support. (3) I'm using the IDE and am trying to compile an example program that comes with "SupermundoGraph" and am getting messages like: wlink SYS dos op m op maxe=25 op q op symf @smg_ex1.lk1 Warning(1028): smg_move_ is an undefined reference Warning(1028): smg_drawmap_ is an undefined reference file timmy.obj(C:\smg\src\smg_ex1.c): undefined symbol smg_resize_ file timmy.obj(C:\smg\src\smg_ex1.c): undefined symbol smg_setclip_ "undefined reference" and "undefined symbol" indicate that something has gone wrong in the linking process. I am not very familiar with the IDEs but, chances are you have not added in the SupermundoGraph library properly. According to the v10.0 IDE help documentation, you simply add the library (*.lib) as if it were a source file. Also watch that you have selected the right directories for your project. I usually set up my directories and build my project sources before invoking the IDE (in the rare occasions that I have ever used the IDE.) If you are not careful, you will end up building projects right in the compiler's own Windows tool directories, which makes for file maintenance headaches sooner or later. If that's not it, then perhaps the example is not compatible with your version of WATCOM C/C++. In this case, you should contact the library vendor and inform them of this problem. (4) I am getting linker errors from wpp386 that make no sense. I have everything externed and properly prototyped as far as I can tell. On possibility is that you are trying to combine C and C++ code. If it is your intention to only write C, and not import C++ libraries, then you should use the wcl386 or wcc386 compilers. If you want to write C++ code, you cannot simply mix C and C++ directly. To import C code into a C++ project you must prototype them as extern "C" { } as follows: extern "C" // c must be a capital C { extern void Set_320_200(); extern void Put_Pixel( BYTE Color, WORD x, WORD y ); } The extern's publicize the symbol, and the "C" declares them as C symbols so that they are linked without C++ attributions. Thus they act as C symbols, not C++ symbols. This means their names are not mangled in the objects, they cannot be overloaded, etc., etc. Thanks to Lassche MA (mlassche@cs.vu.nl) for verifying this, and thanks to Eric Kenslow for giving more a detailed explanation of extern "C". ---------------------------------------------------------------------------- Q20. I am having malloc troubles. It seems that I am trashing the heap but I don't know how to debug it. A20. Someone on rec.games.programmer asked this question and I gave this as the response: Apparently somewhere in version 10.0 or 10.5, WATCOM had a problem with mallocing that has been fixed in a subsequent build. It is likely that a patch for it exists on their WWW site. Nevertheless, I believe that bug was an extremely obscure one with the "free" command and is unlikely to be your problem. Dealing with a trashed heap is difficult with any compiler, but WATCOM makes it particularly challenging because of its "frugal" heap memory tracking system. If you do a little debugging/reverse engineering, you will see that a WATCOM memory allocation tracks the size in the int (signed long) just before the actual allocation itself. So (ptr=(int*)malloc(99))[-1] should be 99. The malloc/free functions use this and this alone to track the heap so if you are trashing memory, especially these sizes, you will start dying really soon. I found that the WATCOM debugger is particularly good at tracking down precisely this sort of problem. Allow me to tell you a story: Once upon a time I had a memory corruption in a large convoluted program I was writing. So I started debugging by putting a wrapper around the "malloc" and "free" functions that added an additional size to test the validity of each malloced size against when I use it (which I put on the *other* side of the allocation.) I put a test in the place where my program was accessing it and GP faulting. I simply output a message indicating that the memory was trashed. Using the debugger I put a break point on the error message printer. Then I used the "reverse execution" feature to get me back to the failed memory size comparision and noted the machine address of the WATCOM size pointer. Then I reset the program and put a breakpoint on access to that memory address. It turns out that my program legally accessed it several times (on the order of a thousand.) So I used the breakpoint "counter" to help me zero in (essentially a binary search) on the number many times this memory was accessed before the crash occurs. By setting the counter appropriately, I found the code which was pounding on the size and rectified it. Total time spent was about 1.5 hours which is not bad for a bug of this complexity (most of the time was spent on other desparate attempts to find the bug *before* diving in and trying the above procedure, which took < 10 mins.) I can't praise the WATCOM debugger enough for enabling me to do the above procedure. In particular, the reverse execution feature gives me unparalleled power (with the exception of Soft-ICE, which of course, gives you much more power) for determining my context. Mixed assembly and source level debugging allowed me to reverse engineer WATCOM's malloc method lickedy split. BTW, the bug in my program was simple. I was accessing a pointer before mallocing it. Paul Keys responded to the post above with a very good suggestion: Watcom has functions that can be used to debug the heap. Functions such as _heapset, _heapchk, _heapwalk, are indespensible. Using _heapset I have managed to find cases where I was using pointers to memory that had been mistakenly freed. With _heapchk, I have tracked down where the heap lost its integrity. And using _heapwalk, I was able to track down some rampant allocation that was slowly gobbling up all available memory. Finally, it should be noted that on the WATCOM C/C++ file site there is a sample program for diagnosing memory errors. This program is located at the following URL: ftp://ftp.powersoft.com/pub/watcom/c_gen/memcheck.zip ---------------------------------------------------------------------------- Q21. How do I write directly to graphics memory? A21. As mentioned above, the base of VGA memory is mapped at 0x0A0000. The ports can be accessed via the outp() function, so even setting up Mode X is no big deal. For an example of this see: http://www.geocities.com/SiliconValley/9498/modex.zip CGA and EGA are no more challenging than VGA. SVGA modes have their own special problems. The examples in Ferraro's book on VGA's and SVGA's can be easily adapted to WATCOM C/C++, even in pure C, with the exception of the VESA SVGA stuff. Revision 1.2 of the VESA SVGA interface (the interface most widely supported by SVGA's out there) requires a 16 bit memory allocation for a data buffer and a 16 bit call or interrupt to the "Bank Switch" procedure (my own investigations lead me to find that the WinPtr call was not reliable on many graphics cards and thus I stuck with the interrupt). Many novices (including myself, when I first tried it) make the mistake of thinking that they can just pass in some appropriately cooked values into the required registers as the VESA spec requires and just call int 10h. Unfortunately this does not work when the "es" register has to be stuffed with the segment for the buffer that VESA fills in with mode info. You need a protected mode to real mode translation API to make dos allocations and simulate real mode dos interrupts. These are both achieved by using DPMI which is discussed elsewhere in this FAQ (see Q23.) Revision 2.0 of the VESA SVGA interface has a 32 bit interface and also allows the option of mapping the entire frame buffer to a single contiguous linear memory range. The VESA SVGA specs are available at: ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/vesasp12.zip and ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/vbe20.zip The WATCOM C/C++ file area also has an example URL for getting you started using VESA: ftp://ftp.powersoft.com/pub/c_cpp/general/newvesa.c If you require linear memory access to SVGA, then your choices are: (1) use a VESA 2.0 interface (the easiest way), (2) Map in the graphics controller's physical memory yourself (the hardest way), or (3) Use a more powerful DOS extender (than DOS4GW) that supports the DPMI phys to linear map function (the expensive way, see next question for more information.) Programming a graphics controller to use its specific functions requires getting specifications for the graphics controller themselves. ---------------------------------------------------------------------------- Q22. How do I install a mouse event handler? A22. There is an example of this on the WATCOM C/C++ web site general file page. The URL is ftp://ftp.powersoft.com/pub/watcom/c_gen/tstmouse.c The general idea is just to make sure the declaration protocals have it taking the right registers as its parameters, stack checking is turned off and DS is reloaded. ---------------------------------------------------------------------------- Q23. What is DPMI and what role does it play in using WATCOM C/C++? How do I communicate between the 16 bit world and 32 bit world on my PC? A23. DPMI stands for "DOS Protected Mode Interface" and was dreamed up by Intel and Microsoft. It was a method for extending DOS's functionality for supporting 32 bit programming at the lowest possible level. Windows is an example of a program which implements and leverages the DPMI interface to enable "Enhanced Mode" and 32 bit DOS Extended applications to run under DOS boxes. (QDPMI, CWSDPMI and DOS4GW itself are other examples.) For most purposes DPMI essentially is an API for setting up 32 bit selectors, and call gates between 16 and 32 bit execution segments. A version 0.90 (the one used by Windows) specification is available at: ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/dpmispec.arj (you need 'ARJ' to decompress this file; its available elsewhere on x2ftp) An online 1.0 specification can be found at: ftp://x2ftp.oulu.fi/pub/msdos/programming/specs/dpmi100.zip Also be sure to check the WATCOM C/C++ file area directory located at ftp://ftp.powersoft.com/pub/watcom/c_gen which has several relevant example files: rintcall.c, memory.c, and dpmi800.c The major differences between 0.90 and 1.0 is that 1.0 servers must return with exact error codes in the event of a failure. Among its most common uses are: allocating 16 bit memory and calling a 16 bit interrupt which takes a 16 bit segmented address for a data buffer (ordinarily DOS4GW and/or WATCOM's heap manager will not "malloc" from DOS memory, so it essentially goes wasted.) These are both required for supporting VESA SVGA 1.2. You may notice that among the many messages printed on DOOM's fake "DOOM OS" init screen is a mention of DPMI allocations. The programmers at "id" probably wrote their own memory manager which uses conventional memory in addition to regular heap memory. The major short coming of DOS4GW is that its an incomplete implementation of DPMI (0.90). The WATCOM C/C++ users's guide describes what DPMI functions *are* implemented in the "INTERRUPT 31H DPMI Functions" chapter. DOS4GW even steps outside of the bounds in the sense that unsupported functions do not simply return with the carry flag set indicating an error (even though it has not completed the DPMI function.) Forunately the latest DOS4GW implements both the DOS alloc function and the simulated DOS interrupt call, which are sufficient for setting up rudimentary 16 bit communication. If another DPMI manager is present, however, DOS4GW will not set up its own DPMI server. (I shelled out $150 for the CauseWay DOS Extender which is far more robust and feature rich.) So how does one actually coordinate 16 and 32 bit components together? Allow me to explain one method. All 32 bit DOS executables are bound with a 16 bit kick-start program. This kick-start program is, by default, named WSTUB.EXE and is located in the %WATCOM%\BINB directory. Obviously you can change the contents of this file to anything you like (you can probably do this without mucking with %WATCOM%\BINB\WSTUB.EXE directly, but rather using the WBIND command, but I have not investigated this fully.) The 16 bit source code for WSTUB is also supplied, which makes the kickstarting procedure fairly clear (starting with version 9.5, WATCOM included both that 16 and 32 bit versions of their compiler in the same package.) It basically parses the command line arguments and passes control to DOS4GW with itself as the first parameter followed by the rest of the regular parameters. You can write all your 16 bit components in the stub, passing critical pointer values as command line parameters to the 32 bit portion. The pointers could point to anything, including 16 bit function entry point addresses and data areas. The 32 portion would do the simple math to convert the pointers and can pass control back to the 16 bit stub functions using the DPMI 16 bit function call simulation call. Voila! The reason I've described the above procedure is that I've seen others take a less savory approach that I'd just as soon see avoided in the future. In at least one sound library I noticed that the implementer wrote a 16 bit TSR that had to be executed beforehand. Clearly, an unsanctioned or unreliable method of communication between the 16bit TSR and 32bit library was being used. Using the stub method is much cleaner. Why would you ever want to paste 16 and 32 bit code together? Well, the main reason would be that you have a large database of 16 bit code that is most logically accessed as if coming from an interrupt event. I've also heard rumours to the effect that using DMA is a 16 bit only operation, but I'd have to investigate that more fully before confirming it. For example, you might have a substitute mouse driver written in 16 bit code that you don't want to run as a separate TSR because you want to retain the old mouse ISR. So you could install it at the start of WSTUB, spawn the 32 bit process, then deinstall it when you come back. Personally, I've modified WSTUB.EXE in minor ways, but in all honesty, I have to admit that I have not tried the above procedure myself. Nevertheless, I cannot see any other reason WATCOM would expose the 16 bit source for WSTUB.EXE. If somebody ever implements (or has already done so) this idea successfully, I'd like to know about it. ---------------------------------------------------------------------------- Q24. What if I really need to compile a 16 bit model program? A24. Starting with version 9.5, WATCOM includes both the 16 and 32 bit compilers in a single package. The 16 bit compiler executables are wpp.exe, wcl.exe and wcc.exe, while the 32 bit compilers are wpp386, wcl386.exe and wcc386.exe. It should just be a matter of changing your makefile or flipping an IDE switch. ---------------------------------------------------------------------------- Q25. Why do WATCOM's results and Visual C++'s results differ on the same source code? Why does WATCOM implement the default signedness of chars different from everyone else? I am having difficulty porting code to WATCOM C/C++? A25. A list of these differences can be found in the online help. Here's a list of the major gotcha's: - To get around the signedness default be sure to pass a "/j" on the command line. - WATCOM will, by default, use registers for its parameter passing convention which makes it incompatible with regular COFF format libraries and object files. However, adding the extra declaration information via "#pragma aux frame;" will force WATCOM to pass parameters to the function via the stack. - WATCOM's "wmake" utility parses a makefile format that is entirely different from Microsoft's. WATCOM decided to mimick UNIX as closely as possible, while Microsoft went ahead and made their own standard. - If you do asynchronous variable updates (such as modifying global variables in an ISR), you *need* to declare your variable as "volatile". Other C compilers optimization strategies act as if all variables were declared volatile, and can mislead you into thinking it is unneccessary to declare your variables this way. - In general, WATCOM's C/C++ compilers tend to be more stable, however one notable exception was the initial release of version 10.0. But WATCOM had patches available in a couple months which fixed its most outstanding problems. Relatively speaking, WATCOM's compiler is fairly robust and more ANSI compliant than Visual C++ which can explain most discrepencies. ---------------------------------------------------------------------------- Q26. Can I link external assembly files together with my C files? A26. WATCOM's linker can accept .OBJ files in its own format or in the standard intel format. So using a separate assembler should not cause any extra unneccessary complications. Earlier versions (9.0) of the compiler required the use of the "WOMP" utility to convert object formats. Starting with version 10.0, WATCOM C/C++ also comes with an external assembler called "WASM". However about the most praiseworthy comment I've heard about it was something along the lines of "when I wrote everything from scratch and followed all its rules, WASM seemed to be able to do the job." Basically, I don't believe WATCOM implemented many of the common, but superfluous assembler features of MASM or TASM which a lot of people rely on and hence were complaining about. The most common problem when trying to link external assembly objects with C/C++ objects is the name mangling/modification that the C/C++ compiler does by default. WATCOM C symbols in general are appended with an undescore ("_"), whereas assembler names are not modified in any way. This can be changed with a #pragma directive (see Q27, and the online documentation for changing the symbol renaming convention used by WATCOM C/C++.) However, Eric Kenslow says that this is unneccesary. WATCOM has changed the meaning of "cdecl" recently, so how you deal with this may be different depending on the version of WATCOM C/C++ you are using. In any event, if you are having problems linking asm and C objects together, use "wdisasm" (or simply a binary viewer for that matter) and see how the shared symbol names are being modified for each of your objects. As recourse, in C you can use the #pragma name modification directive, and in assembly you can simply append your public/extern names with underscores as necessary. Modifying your assembly symbol names is the method Eric Kenslow recommends. ---------------------------------------------------------------------------- Q27. How do #pragma's work? How do I do inline assembly language? A27. WATCOM's #pragma command is generally used as an alternative way for defining C/C++ function generation properties. Examples: 1. If you know that the parameters for a function are available either on the stack or in certain x86 registers then you can assign which go where *explicitely*. 2. If you know that at link time you will need the exported function names to be mangled in some nondefault way then you can deal with that in a #pragma. 3. If you want to generate inline assembly then you can do so as actual assembly text or opcode bytes. You give a function prototype and then declare its content using the #pragma aux ="" directive. This system for dealing with inline assembly is very powerful as it allows you to explicitely declare your register usage, so that your C and assembly stitch together perfectly. Surprisingly, Visual C++ and Borland's "_asm" method is not nearly as powerful. With the "_asm" method it is recommended that you look at the disassembly (you need to generate a .COD file in Visual C++) of your surrounding source C to know what registers are available to use, or simply do not modify any registers at all. This makes inline assembly coding tedious and difficult to maintain. In contrast, the WATCOM method requires that you abstract your inline assembly to have a regular C-function prototype which aids in portability to non-x86 platforms. A word of warning; inline assembly functions declared with #pragma aux are not externed and hence behave like regular C++ "inline" or "static" functions. The typical method of dealing with multiple C files using the same inline function is to define it in a local include file common to your C files. In this vein I think of #pragma's as type checked macros, rather than functions. Here's an example of the power of #pragma inline assembly for 32 bit FLAT memory model. Suppose we wish to implement the following: static void DWORDCopy(long *Source, long *Destination, int Length) { for(;Length;Length--) { *Source++=*Destination++; } } in tight (i.e., small) assembly. Using the WATCOM disassembly will show you that the above is *not* compiled to a "rep movsd" no matter what compiler settings you use. The functionally equivalent inline pragma is implemented as follows: void DWORDCopy(long *Source, long *Destination, int Length); #pragma aux DWORDCopy = " rep movsd " parm [ESI] [EDI] [ECX]\ modify [ESI EDI ECX]; The above function will copy a packed array of 32 bit quantities from a source pointer to a destination pointer using the "rep movsd" x86 assembly construct. The source and destination parameters are put into ESI and EDI, with the length in ECX which is exactly what "rep movsd" requires for priming. In addition movsd requires that es and ds are set up, but that is all take care of by the FLAT model start up code. To implement this same thing for Borland C++ or Visual C++ you need additional priming code to transfer the source and destination parameters to the correct registers. Finally, the C-compiler needs to know what registers were modified by the inline #pragma so that it can figure out what registers are available to the surrounding C-code. This cuts down on unnecessary save and restores of registers that is generally required to safely implement an inline "_asm". Typically, the meat of the code appears in the double quotes, and is several lines long, however opcodes can also be entered directly as bytes as well. Remember that ANSI C constants strings can be broken up in sequence, and that lines can be stitched together with a "\" at the end. #pragma's have to be declared on one line, so longer inline assembly language sequences typically look more like: void SetMode13(void); #pragma aux SetMode13 = \ " mov ax,13h " \ " int 10h " \ modify [ax]; (My point above is that this #pragma statement is equivalent to: #pragma aux SetMode13 = " mov ax,13h int 10h " modify [ax];) You then call "SetMode13()" as if it were a regular function, and WATCOM does the inline pasting for you. Why would you ever want to enter opcodes? Well, intel has a habit of not documenting all of their mneumonics: unsigned int RDTSC(void *); #pragma aux RDTSC = \ 0x0f 0x31 /* RDTSC */ \ " mov [esi],eax " \ " mov [esi+4],edx " \ parm [esi] modify [eax edx] value [eax]; (Although the WATCOM C/C++ compiler is actually aware of the rdtsc instruction, it comes back with a error about the CPU setting when I try to use it in inline assembly. This is understandable given Intel's unofficial status of this instruction.) Direct opcode inlining will become more important as people start using Intel's "MMX" CPU extensions. For more examples see "KNUT's WATCOM C/C++ tutorial" at http://www.oslonett.no/home/oruud/wat_tut.htm The information given there is not 100% correct, however he gives detailed examples that should give you a rough idea about how it works. More detailed information can be found in the online help. ---------------------------------------------------------------------------- Q28. How do I do compiled bitmaps? How do I do on-the-fly generated code? A28. Its no big deal really. Remember that CS, DS and ES are mapped to the same physical space. So in principle you need only malloc some space, fill it with 32 bit instructions (note that the opcodes between 16 and 32 bit do not translate directly) cast it to a function pointer (be especially careful with any parameters passed) and call it. Modifying existing code (realizing of course that you take your life in your own hands when you do this) is also possible. Cast a function pointer to a regular data pointer ((char *) is probably the only appropriate pointer type) modify the data function code as if it were just an array of opcodes and operands, then call the function. Unfortunately, it looks like the compiler can do something really odd. If you assign the address of an existing function to a function pointer and modify it directly under the same scope, the compiler will tack on a "CS:" segment override, which will cause a page fault at execution time (Code selectors cannot be used to write data.) I don't quite see why they did this but the obvious work around of using another function to modify the actual code seems to work just fine: #include #include #include // printf compatible function prototype typedef int (* fntype)(char *, ... ); char * fn; // Assembly language opcode to cause an immediate return: #define RET 0xc3 void SetReturn(char * ptr) { ptr[0] = RET; } void main(void) { printf("Hello\n"); // Call "printf" as normal fn = (char *)malloc(128); // Get some memory SetReturn(fn); // Assign function ((fntype)fn)("Hello\n"); // Call function with parameter fn = (char *)&printf; // Point to "printf" SetReturn(fn); // Reassign printf function printf("Hello\n"); // Call "printf" as normal } This was compiled with the -5r and -s switches (only.) Clearly there is a difference between the stack based and non-stack based libraries which makes this sort of self-modifying code particularly hazardous to deal with. The details for doing compiled bitmaps are beyond the scope of this FAQ, however the general idea is to construct a number of mov [edi+offset],immediate one after the other which plot the sprite. This makes transparency encoding an intrinsic part of the sprite. However, difficulty with clipping (especially in the x direction) and destination memory write alignment makes compiled sprites a questionable compromise to a more general run length copy routine. For more information read the newsgroup: rec.games.programmer. Doing more exotic things like writing your own compiler and setting up your own compiler environment with protected execution spaces are beyond the scope of this FAQ, but can probably be dealt with just using DPMI. Now, before you go off and get any not so bright ideas, read the following USENET conversation: > Hi there, I was wondering how you need to set things up so you can modify > your code and make copies of your functions under DOS4GW/Watcom.... I've > tried copying the function's code around to several different areas, but > it always gets a GPF.... Here's a simple example that crashes, if anyone > has any idea how to make it work, please let me know! Thanks in advance! > > void printstuff() > { > printf("HELLO\n"); > } > > void main() > { > void (*pFn)(); > > pFn = new char[500]; > memcpy( pFn, printstuff, 500 ); > pFn(); > } The reason this doesn't work is because of the x86's relative addressing modes. That is to say, that the >> printf("HELLO\n") << statement is translated to a *relative* call. It gets translated to something like: mov eax,offset L1 ; Pointer for string "HELLO\n" call relative ((offset printf)-($+5)) ; $ = current address. The x86 will automatically add in the current address to the relative offset and come back with the right address. So in the copied version of the routine (in pFn()) you won't end up at the address of "printf" which you desire, because the relative difference was copied, rather than the physical address. There is no easy work around for this, and it is for reasons such as this that self-modifying code and related techniques, such as what you've tried to do are generally frowned upon. The following two suggestions which fix the above program also serve to illustrate the flaw I am pointing out. (1) Instead of using 500, try using the number 31744 in both places. (2) Rather than calling printf directly, assign the address of printf to a global volatile (just to make sure WATCOM doesn't try any simplification tricks to avoid using it) function pointer, and call the function pointer instead of printf directly. ---------------------------------------------------------------------------- Q29. How do I install a 32 bit interrupt vector? How do I install a bimodal interrupt? A29. Look up _dos_getvect, _dos_setvect, _dos_keep and _chain_intr in the online help. I don't know much about bimodal interrupts but there is some sample code in the WATCOM C/C++ general file area which demonstrates it. The URL is ftp://ftp.powersoft.com/pub/watcom/c_gen/bimodal.zip. I think the idea is to write some assembly code which can be correctly executed under either 16 or 32 bit segments and to make sure the interrupt vector is hooked out without DOS4GW's interrupt code segment translation intervening. Here is an example for hooking out the timer interrupt. Note the use of "volatile" on the Timer_Tick variable declaration. This is an often over looked detail in this sort of asynchronous kind of programming. #include #include #include volatile int Timer_Tick = 0; /* Must be "volatile" */ void __interrupt New_Timer_ISR(void); void (__interrupt * Old_Timer_ISR)(void); void __interrupt New_Timer_ISR(void) { Timer_Tick ++; Old_Timer_ISR(); /* Chain back to original interrupt */ } /* Ordinarily using INT 01Ch is the preferred method, but INT 08h can * also be used. */ #define TIMER_INTERRUPT (0x1C) void main(void) { Old_Timer_ISR = _dos_getvect( TIMER_INTERRUPT ); _dos_setvect( TIMER_INTERRUPT, New_Timer_ISR ); do { printf( "%d\n", Timer_Tick ); } while( !kbhit() ); _dos_setvect( TIMER_INTERRUPT, Old_Timer_ISR ); } If understanding is your goal then keep the following in mind: - DOS4GW initializes most interrupt vectors to point into the extender itself. These new vectors are just a translation layer through DPMI to switch into real mode and call the old vector. - Since function pointers (including those declared as __interrupt) are 32 bit offsets, the standard old DOS set/get vector int 21h functions will not work in the standard old way. Using _dos_getvect and _dos_setvect is really the only reliable way to do ISR management. - When a new 32 bit ISR is installed, no translation layer is installed because it is not needed. - DOS4GW emulates the old real mode interrupts as closely as possible by preserving the regular register values across the interrupt translation layer. However, the segment registers are clearly not preserved. This is not only because a direct Selector->Segment translation makes no sense but because CS, and SS, must be set up to simulate a 16 bit context. - DOS4GW implements its own version of int 21h, which looks very much like a 32 bit extension of int 21h. This is why it is referred to as a "DOS Extender". In these cases places where there might be Selector-> Segment translation issues, there has been a work around to use the _FLAT model paradigm. See the online documentation for more information. - When you are *required* to pass a segment value to a 16 bit interrupt routine, you need to use the DPMI 300h "Simulated interrupt" function. Other DOS extenders such as CauseWay and Pmode/w work in similar ways. ---------------------------------------------------------------------------- Q30. How do I get rid of the DOS4GW banner? How do I bind DOS4GW.EXE to my application? How do I get rid of the external DOS4GW.EXE altogether? What other DOS extenders can I use? A30. To get rid of the DOS4GW startup banner, bind your application to "wstubq.exe" instead of the default "wstub.exe", or altertaively write your own wstub.exe which does a "set DOS4GW=QUIET". You cannot simply get rid of DOS4GW.EXE. You need some sort of DOS extender to drive your 32 bit app. DOS4GW.EXE does not give you a link option however other extenders such as DOS4G (the professional version of DOS4GW), CauseWay, and PMODE (available at the x2ftp site) are plug in DOS4GW compatible DOS extenders that will let you link the extender right to your code. This way, from the user perspective, your application is no different from old 16 bit .exe's out there. This is very important for those interested in making tools that are easily transportable in a stand alone form. The other main advantage of using DOS Extenders *other* than DOS4GW, is that all of them seem to support the DPMI "800" call, which is essential for mapping physical memory to linear memory so that you can access all of graphics memory for some SVGA cards directly. WATCOM's 3rd party support home page includes other DOS extenders that work with their compiler. Pointers to these can all be found either directly from a www search engine or on the WATCOM www page. Here's a quick comparision of what is out there that I am aware of. If you have information to add or about other extenders (such as blinker, DOS4G) please tell me so I can include it. Pharlap/TNT - Large proven API, which supersets DPMI. The extender is external and a separate link process is required, but WATCOM supports it as part of the basic compiler environment. I don't know the current going rate, but last I checked it had a $500 price tag. PModew/W v1.22 - Small, fast, links to exe, supports exe compression, plug in compatible with DOS4GW and is free for development stages with a $500 flat charge if supplied with a shipping product. It does not hook out error exception routines which makes developping with it problematic at best. People who use it suggest developing with DOS4GW and adding in pmodew later on. For more information, check out http://www.di.net/pmw/ CauseWay - Fast, extended DPMI API, supports exe compression, links to final exe, unlimited vitural memory, plug in compatible with DOS4GW, $200. This is a real no nonsense dos extender which truly supersets DOS4GW (error handling included, unlike pmodew) and which is at least as robust. The WATCOM debugger can be used with uncompressed CauseWay extended apps (be sure to follow the CauseWay installation instructions carefully), but CauseWay comes with its own debugger that can deal with compressed apps as well. It comes with an informative manual and plenty of sample code for assembly and WATCOM C/C++ usage (including examples of using VESA SVGA.) For more information, check out http://www.devoresoftware.com/ ---------------------------------------------------------------------------- Q31. How do I make WATCOM C/C++ work with the latest OS/2 Toolkit? A31. I know nothing about WATCOM's OS/2 support, however Eric Lekven (elekven@qualcomm.com) has given me the following information: WATCOM does not ship its compiler with the newest OS/2 Toolkit and must be obtained on the IBM OS/2 Developer Connection CDROM or direct from IBM. Unfortunately both require payment, although there have been promotional versions of the Developer Connection CDROM shipped with Dr Dobb's Journal magazine which contain the Toolkit for free. Perhaps they will do this again when OS/2 Merlin ships. In order to use IBM's OS/2 Warp Toolkit header files you need to edit three of the files. os2def.h: Find the two identical lines containing #if defined(__IBMC__) || defined(__IBMCPP__) Edit these two lines to contain this #if defined(__IBMC__) || defined(__IBMCPP__) || defined(__WATCOMC__) mmio.h, mmioos2.h: Both of these have two lists of #defines, one for country codes, the other for keyboard codes. The values are decimal. However these files are non-ANSI in that these decimal numbers have leading zeroes. Delete the leading zeroes from all these #define statements. Breckan Morris has verified this and recommends this over compiling with the stack interface (/4s or /5s) which would also solve the problem. He also adds that there is also a line of the form: #if __IBMC__ || __IBMCPP__ which should be changed to #if __IBMC__ || __IBMCPP__ || __WATCOMC__ According to Vance Palodichuk, OS/2 4.0 (aka Merlin) has fixed the non-ANSI leading zeros error from their mmio*.h files, and the modification listed by Breckan above is sufficient. I.e., you just have to add a "|| __WATCOMC__" to one line of the os2def.h file to make it work. It makes me wonder why IBM didn't simply add this modification themselves just to make it that much easier to use the OS/2 toolkit. This has to be contrasted with Microsoft and Intel who have clearly gone out of their way to support WATCOM C/C++. It seems surprising to me that the relationship between WATCOM and IBM isn't better since they have clearly been beneficial to each other. ---------------------------------------------------------------------------- Q32. Are there any other WATCOM C/C++ caveats? A32. Here are some things I've learned while playing with WATCOM C/C++: 1) Structure declarations that include shorts or chars are highly unlikely to be in the physical order that they are declared. This is because WATCOM re-orders the entries for efficiency. This is within ANSI C specifications. ANSI specs do require that the first entry of a structure always be in the first position. Hence, using structures of structures in the right way, you can guarrentee the positioning of arbitrary structure elements. 2) ISR's do not properly load ES. Hence you should insert a #pragma aux macro (that looks something like: "push ds pop es") if you are using any library functions. Autoinitialization of arrays or structures is out of the question, since WATCOM C/C++ uses rep movsd. 3) To mix use of floating point in an ISR and in your main thread you must save the floating point state (fsave? fstore?) at the beginning of your ISR and restore it at the end (frestore?). This is because the state of the FPU cannot be reliably known when the ISR is executed (the stack may be full because from the main thread). Because saving and restoring the FPU state is so costly, I would recommend against using the FPU entirely, within ISRs. 4) DOS4GW ISR's do not share the same stack with your main application. This means that there are certain function calls that will not work: longjmp and exit are the ones that immediately come to mind. (Of course any non-reentrant functions are off limits as well, but those are standard ISR restrictions.) 5) DOS4GW has no provisions for TSR programming, however it is possible with the professional version, DOS4G. (I am not aware of these capabilities in other DOS extenders.) 6) The #pragma aux inline assembly language directive is limited to assembly sequences of up to 127 bytes or 255 bytes depending on the version of the compiler. WATCOM recommends using external assembly for longer assembly sequences. Charlie Wallace makes the good observation that you can deal with this easily by breaking up your inline fragments into pieces: void setup(void); #pragma aux setup = \ " mov edi,0xa0000 " \ " mov ecx,2000 " \ modify [edi ecx]; void copy(void); #pragma aux copy = \ " loop1: " \ " mov [edi],0 " \ " inc edi " \ " dec ecx " \ " jnz loop1 " \ modify [edi ecx]; void main(void) { setup(); copy(); } The "modify" statements above are identical (the code generator is smart enough to see that it doesn't need to restore then save the registers between setup and copy), but as usual, they must describe the registers modified in each inline #pragma. If your flow logic is complicated in such a way that you might jump from one #pragma to another, then you should include all potentially modified registers for the collection of #pragma's, in each #pragma's modify clause. 7) Although mentioned elsewhere, I might as well reiterate that WATCOM has by default implemented "char" as "unsigned char". This can be overidden with the /j switch, and the IDE does this by default. 8) Apparently the WATCOM C/C++ compiler has difficulty on DOS systems with 64MB of memory. This problem does not occur with Windows DOS boxes. The solution, according to Charlie Wallace, is to replace w32run.exe (in either %WATCOM%\binw or %WATCOM%\bin, depending on the which version you are using) with one of tntrun.exe, d4grun.exe or x32run.exe (i.e., use the copy command; if things screw up you can always quicky incrementally reinstall from the CDROM.) Apparently, the one which works is system dependent. 9) ES is assumed to be equal to DS and the direction flag is assumed clear. For autoinitialization, structure copying and many library functions, WATCOM C/C++ simply uses the "rep movsd" assembly instruction which means that the direction flag must be clear and ES, must be set to the _FLAT selector, as they are by default. WATCOM does not allow you to add (E)SP, (E)BP, or segment registers to a "modify clause" in a #pragma. So if they are modified (by a BIOS call, or by some inline assembly) you have to save and restore those registers yourself. Similarly the direction flag should be explicitely cleared with the "cld" instruction if it is modified. ---------------------------------------------------------------------------- Q33. Will WATCOM C/C++ support MMX? A33. Yes. Intel made the following announcement in March 1996: "Powersoft Corporation has announced a commitment to deliver a version of Watcom C/C++ that supports Intel's new MMX(tm) technology. This support will enable development of lightning fast applications that exploit the capabilities of the MMX instruction set and that leverage the industry-leading optimization technology of Watcom C/C++. Watcom C/C++ will feature complete debugging support (including MMX technology register display and the disassembly of MMX instructions) as well as a functional interface from C/C++ that generates inline MMX instructions." ---------------------------------------------------------------------------- Q34. So what is your story? Why did you do this? A34. I was a summer hire for WATCOM many eons ago, and I appreciate that they gave me my first real job. I have always been impressed by their compilers and I've always wanted to see them crush both Borland and Microsoft in the compiler arena. (It looks like Borland will be laying down for the count, but Microsoft is still there of course.) WATCOM's most notable weakness is user support. Their documentation is very dry and generally does not fully explain all of the compiler's capabilities. I'm told this is the main reason a very high profile game company stopped using their compiler in favour of another 32 bit DOS based C compiler. I've also found that lately, I'd been posting on a daily basis just answering people's basic questions about WATCOM C/C++. It is in the hopes of curing this problem that I've written up this FAQ. Finally, I am hoping that people make contributions and possibly submit corrections so that I can verify my own knowledge. This process also results in a more reliable FAQ. Numerous contributions have been made thus far (including one from a WATCOM employee, and one from an Intel employee representing Intel), and I have received a lot of positive feedback which has made it worth while. ---------------------------------------------------------------------------- Cool contributions ~~~~~~~~~~~~~~~~~~ C01. Profiling and debugging all rolled into one using an external terminal connected by serial. Contributed by Charlie Wallace. [PH - The following contribution describes an alternative method for profiling and debugging using a serial port connection to another machine. I believe the idea is to redefine default the epilogue and prologue code for your functions to communicate with a debugging or profiling device. The details were too complicated for me to verify. However, for advanced WATCOM C/C++ users looking for an alternative debugging or profiling method, the following may be useful.] This is a small tip on using /ee /ep /en options of watcom, usually these are used by the watcom profiler. I`ve found them useful for debugging in the past, especially when you can`t load into the debugger, like when watcom debugger had no support for X32, what i`d do was compile with those options which adds a call before and after every function call, so when the code locked up i knew roughly where it was happening, i made the __PRO routine emit the function name to the serial port at a preset speed to a terminal, with a tab added at every level entered, then the __EPI routine removed a tab and a new line so on the serial terminal it looked thus : main(i) my_func1(i) my_func2(i) my_func2(o) my_func1(o) main(o) where (i) and (o) are in and out, when the code locked up only the last call entered would be displayed. there are many other uses of this function, profiling with other hardware, remember though this changes the execution flow, you can compensate for it though, making a simple debugger, debug options, watchdogs for memory leak and code overwrites and so on . . . However one caveat is to make sure you don`t add profile hooks to the profiler code, write it in asm in a seperate asm file, (don`t use #pragma aux), and don`t call a C function of your own that has been compiled with /ep /en, otherwise it`ll recurse. a small example of how it works, assuming a simple compile with wcl386 /ep /ee /en test.c pro.asm where test.c is something like , simple.. int foo(int a) { return a-1; } main() { int a; a=foo(1); } which is compiled to db 'foo' db 3 _foo: push ebp mov ebp,esi call __PRO ; does the function call _EPI pop ebp ret db 'main' db 4 _main: push ebp mov ebp,esi call __PRO ; code goes here.. call _EPI pop ebp ret so get the function name, get the return address, esp will point to it, subtract the length of the code before the __PRO call, ie the push ebp + mov ebp,esi and add 1, use wdisasm to calculate this, usually its 9, but i`m not saying it always will be, so check. this byte is the length of the function name, subtract this from the previous address just calculated and this gives you the function name + length, ready to print out, or do whatever you like.. Here is a 32bit version of __PRO and __EPI, for 32bit flat mode. pro.asm - snip here 8-X ; ; use this fragment to code your own routines. ; .586p .model flat .code PUBLIC __PRO PUBLIC __EPI ; this routine gets called first ; ; usually like this, *- means subtract from current offset ; ;function_name: ; db 'my_c_function' ; db *-function_name (compute length of prev string) ;my_c_function: ; push ebp ; mov ebp,esp ; call __PRO ; ; __PRO proc near push esi push eax mov esi,[esp] sub esi,9 ; this is the length of the ; call __PRO+mov ebp,esp+push ebp+1 ; it may need to be changed to match different ; compile options. check with wdisasm xor eax,eax mov byte ptr al,[esi] ; length of function name sub esi,eax ; esi -> function name ; eax -> length of string ; do what you will with these, EXCEPT ! call any function in C ; that you compiled with /ep or /en, otherwise it`ll recurse ; for ever (at least for a while anyway). pop eax pop esi ret __PRO endp ; this gets called at the end of the function ; usually like this :- ; ; call __EPI ; pop ebp ; ret __EPI proc near ; do what you will here, just save all the reg's ret __EPI endp end pro.asm - snip here 8-X ---------------------------------------------------------------------------- C02. Convert WATCOM's help file format to a text file for easier reading and potentially for easier manipulation. Joergen Bech sent me a program which will convert WATCOM's .IHP files to a text file, a table of contents file and an index file. This will allow you to peruse the file from your favourite text editor, or manipulate it as you see fit. The program is freely available at: http://www.geocities.com/SiliconValley/9498/ihp_conv.zip It assumes you have DOS4GW.EXE at your disposal, in your path. ---------------------------------------------------------------------------- Thanks for the feedback! ======================== I have received numerous emails in response to my creation and public dissemination of the WATCOM C/C++ FAQ. They have, for the most part, been very encouraging. The contributions and corrections that have been sent to me have served to increase the quality of the FAQ tremendously. Readers can rest assure knowing that the accuracy and content is being examined very carefully by discerning WATCOM C/C++ users. I would like to especially thank WATCOM Systems and Intel corp. for their invaluable individual